package com.atguigu.tingshu.account.service.impl;

import com.atguigu.tingshu.account.mapper.RechargeInfoMapper;
import com.atguigu.tingshu.account.mapper.UserAccountDetailMapper;
import com.atguigu.tingshu.account.mapper.UserAccountMapper;
import com.atguigu.tingshu.account.service.UserAccountService;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.result.Result;
import com.atguigu.tingshu.model.account.RechargeInfo;
import com.atguigu.tingshu.model.account.UserAccount;
import com.atguigu.tingshu.model.account.UserAccountDetail;
import com.atguigu.tingshu.vo.account.AccountLockResultVo;
import com.atguigu.tingshu.vo.account.AccountLockVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
@SuppressWarnings({"unchecked", "rawtypes"})
public class UserAccountServiceImpl extends ServiceImpl<UserAccountMapper, UserAccount> implements UserAccountService {

    @Autowired
    private UserAccountMapper userAccountMapper;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private UserAccountDetailMapper userAccountDetailMapper;

    @Autowired
    private RechargeInfoMapper rechargeInfoMapper;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void checkAndDeduct(AccountLockVo accountDeductVo) {
        try {
            //1.检查及扣减余额
            int count = userAccountMapper.checkAndDeduct(accountDeductVo.getUserId(), accountDeductVo.getAmount());
//        int i = 1/0;
            if (count == 0) {
                throw new GuiguException(400, "账户余额不足！");
            }
            //2.新增账户变动日志
            this.addUserAccountDetailLog(
                    accountDeductVo.getUserId(),
                    accountDeductVo.getContent(),
                    SystemConstant.ACCOUNT_TRADE_TYPE_MINUS,
                    accountDeductVo.getAmount(),
                    accountDeductVo.getOrderNo()
            );
        } catch (GuiguException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public IPage<UserAccountDetail> findUserConsumePage(Page<UserAccountDetail> userAccountDetailPage, Long userId) {
        //  创建查询条件
        LambdaQueryWrapper<UserAccountDetail> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(UserAccountDetail::getUserId,userId).eq(UserAccountDetail::getTradeType,"1204");
        return userAccountDetailMapper.selectPage(userAccountDetailPage,wrapper);

    }

    @Override
    public IPage<UserAccountDetail> findUserRechargePage(Page<UserAccountDetail> userAccountDetailPage, Long userId) {
        //  创建查询条件
        LambdaQueryWrapper<UserAccountDetail> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(UserAccountDetail::getUserId,userId).eq(UserAccountDetail::getTradeType,"1201");
        return userAccountDetailMapper.selectPage(userAccountDetailPage,wrapper);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addAccount(String orderNo) {
        //  1.  recharge_info
        LambdaQueryWrapper<RechargeInfo> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(RechargeInfo::getOrderNo,orderNo);
        RechargeInfo rechargeInfo = rechargeInfoMapper.selectOne(wrapper);
        //  判断
        if (null == rechargeInfo){
            return;
        }
        //  赋值：
        rechargeInfo.setRechargeStatus(SystemConstant.ORDER_STATUS_PAID);
        rechargeInfoMapper.updateById(rechargeInfo);
        //  2.  user_account
        userAccountMapper.add(rechargeInfo.getUserId(),rechargeInfo.getRechargeAmount());
        //  3.  记录充值信息.
        this.addUserAccountDetailLog(rechargeInfo.getUserId(), "充值",SystemConstant.ACCOUNT_TRADE_TYPE_DEPOSIT,rechargeInfo.getRechargeAmount(),orderNo);
    }

    @Override
    public void unlock(String orderNo) {
        //  定义锁的key
        String key = "unlock:" + orderNo;
        //	数据库Key
        String dataKey = "account:lock:" + orderNo;
        //  使用setnx key value
        Boolean result = this.redisTemplate.opsForValue().setIfAbsent(key, orderNo, 10, TimeUnit.MINUTES);
        //  判断
        if (!result) {
            return;
        }
        //  没有key执行业务逻辑. 从缓存中获取数据.
        //	从缓存中获取数据
        AccountLockResultVo accountLockResultVo = (AccountLockResultVo) this.redisTemplate.opsForValue().get(dataKey);
        //	判断
        if (null == accountLockResultVo) {
            return;
        }
        //  调用解锁方法.
        int unlock = userAccountMapper.unlock(accountLockResultVo.getUserId(), accountLockResultVo.getAmount());
        //  判断
        if (0 == unlock) {
            //  删除防止重复提交key
            this.redisTemplate.delete(key);
            return;
        }
        //  记录日志
        this.addUserAccountDetailLog(accountLockResultVo.getUserId(), "解锁:" + accountLockResultVo.getContent(), SystemConstant.ACCOUNT_TRADE_TYPE_UNLOCK, accountLockResultVo.getAmount(), orderNo);
        //  删除缓存的数据
        this.redisTemplate.delete(dataKey);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void minus(String orderNo) {
        //	声明key
        String key = "minus:" + orderNo;
        //	数据库Key
        String dataKey = "account:lock:" + orderNo;
        //	利用setnx key value;
        Boolean exist = this.redisTemplate.opsForValue().setIfAbsent(key, orderNo, 10, TimeUnit.MINUTES);
        //	有人操作.
        if (!exist) {
            return;
        }
        //	从缓存中获取数据
        AccountLockResultVo accountLockResultVo = (AccountLockResultVo) this.redisTemplate.opsForValue().get(dataKey);
        //	判断
        if (null == accountLockResultVo) {
            return;
        }
        //	扣减金额：
        int minus = userAccountMapper.minus(accountLockResultVo.getUserId(), accountLockResultVo.getAmount());
        if (0 == minus) {
            //  删除防止重复提交key
            this.redisTemplate.delete(key);
            return;
        }
        //  记录日志
        this.addUserAccountDetailLog(accountLockResultVo.getUserId(), "扣减:" + accountLockResultVo.getContent(), SystemConstant.ACCOUNT_TRADE_TYPE_MINUS, accountLockResultVo.getAmount(), orderNo);
        //  删除缓存的数据
        this.redisTemplate.delete(dataKey);

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Result checkAndLock(AccountLockVo accountLockVo) {
        //	本质操作：1.查询是否有可用余额  2.更新锁定余额
        //	1.	锁定的时候，将锁定金额可以存储到缓存中.
        //	使用setnx 防止重复检查与锁定. orderNo 是唯一： setnx key value;
        String key = "checkAndLock:" + accountLockVo.getOrderNo();
        //	数据库Key
        String dataKey = "account:lock:" + accountLockVo.getOrderNo();
        //	执行setnx 命令
        Boolean exist = this.redisTemplate.opsForValue().setIfAbsent(key, accountLockVo.getOrderNo(), 10, TimeUnit.MINUTES);
        //	判断 exist = true；执行成功; exist=false: 表示执行失败
        if (!exist) {
            //	从缓存中获取数据 set dataKey accountLockResultVo  set dataKey Json.toJsonString(accountLockResultVo);
            AccountLockResultVo accountLockResultVo = (AccountLockResultVo) this.redisTemplate.opsForValue().get(dataKey);
            //	判断
            if (null != accountLockResultVo) {
                //	获取到当前对象
                return Result.ok(accountLockResultVo);
            }
            //	会失败：
            return Result.fail();
        }

        //	检查金额；
        UserAccount userAccount = userAccountMapper.check(accountLockVo.getUserId(), accountLockVo.getAmount());
        //	判断
        if (null == userAccount) {
            //	没有可用余额
            this.redisTemplate.delete(key);
            return Result.fail();
        }
        //	锁定金额：
        int account = userAccountMapper.lock(accountLockVo.getUserId(), accountLockVo.getAmount());
        if (account == 0) {
            //	没有可用余额
            this.redisTemplate.delete(key);
            return Result.fail();
        }
        //	记录账户明细. user_account_detail
        this.addUserAccountDetailLog(accountLockVo.getUserId(), "锁定:" + accountLockVo.getContent(), SystemConstant.ACCOUNT_TRADE_TYPE_LOCK, accountLockVo.getAmount(), accountLockVo.getOrderNo());
        //	将这个查询对象放入缓存.
        AccountLockResultVo accountLockResultVo = new AccountLockResultVo();
        accountLockResultVo.setUserId(accountLockVo.getUserId());
        accountLockResultVo.setAmount(accountLockVo.getAmount());
        accountLockResultVo.setContent(accountLockVo.getContent());
        //	存储数据到缓存 就是为了后面实现真正扣减库存时，从缓存中获取即可！
        this.redisTemplate.opsForValue().set(dataKey, accountLockResultVo, 10, TimeUnit.MINUTES);
        //	返回数据
        return Result.ok(accountLockResultVo);
    }

    /**
     * @param userId
     * @param title
     * @param tradeType
     * @param amount
     * @param orderNo
     */
    private void addUserAccountDetailLog(Long userId, String title, String tradeType, BigDecimal amount, String orderNo) {
        //	创建对象
        UserAccountDetail userAccountDetail = new UserAccountDetail();
        //	赋值：
        userAccountDetail.setUserId(userId);
        userAccountDetail.setTitle(title);
        userAccountDetail.setTradeType(tradeType);
        userAccountDetail.setAmount(amount);
        userAccountDetail.setOrderNo(orderNo);
        //	保存数据
        this.userAccountDetailMapper.insert(userAccountDetail);

    }

    @Override
    public BigDecimal getAvailableAmount(Long userId) {
        //	select available_amount from user_account where user_id = ?;
        UserAccount userAccount = userAccountMapper.selectOne(new LambdaQueryWrapper<UserAccount>().eq(UserAccount::getUserId, userId));
        //	判断
        if (null == userAccount) {
            return new BigDecimal("0");
        }
        //	返回可用余额
        return userAccount.getAvailableAmount();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void initUserAccount(long userId) {
        try {
            //	创建对象
            UserAccount userAccount = new UserAccount();
            userAccount.setUserId(userId);
            //	默认是非 vip 所以一个一点金额就够用了。
            userAccount.setTotalAmount(new BigDecimal("1000"));
            userAccount.setAvailableAmount(new BigDecimal("1000"));

            //	保存数据
            userAccountMapper.insert(userAccount);
            int i = 1/0;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

    }
}
